#ifndef __MQ_BROKER_HPP__
#define __MQ_BROKER_HPP__

#include "mq_connection.hpp"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

namespace mq
{
    #define DBFILE "/meta.db"
    #define HOSTNAME "VirtualHost"
    class Server
    {
    public:
        using MessagePtr=std::shared_ptr<google::protobuf::Message>;
        Server(uint16_t port,const std::string& basedir)
            :_server(&_baseloop,muduo::net::InetAddress("0.0.0.0",port),"Server",muduo::net::TcpServer::kReusePort)
            ,_dispatcher(std::bind(&Server::onUnknownMessage,this,std::placeholders::_1,
            std::placeholders::_2,std::placeholders::_3))
            ,_codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage,&_dispatcher,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)))
            ,_host(std::make_shared<VirtualHost>(HOSTNAME,basedir,basedir+DBFILE))
            ,_consumer_manager(std::make_shared<ConsumerManager>())
            ,_connection_manager(std::make_shared<ConnectionManager>())
            ,_pool(std::make_shared<ThreadPool>())
        {
            //1.初始化消费者队列
            QueueMap qm=_host->allQueues();
            for(auto& iter:qm)
            {
                _consumer_manager->initQueueConsumer(iter.first);
            }

            //2.注册请求处理函数
            _dispatcher.registerMessageCallback<openChannelRequest>(std::bind(&Server::onOpenChannel,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<closeChannelRequest>(std::bind(&Server::onCloseChannel,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<declareExchangeRequest>(std::bind(&Server::onDeclareExchange,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<deleteExchangeRequest>(std::bind(&Server::onDeleteExchange,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<declareQueueRequest>(std::bind(&Server::onDeclareQueue,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<deleteQueueRequest>(std::bind(&Server::onDeleteQueue,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<queueBindRequest>(std::bind(&Server::onQueueBind,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<queueUnBindRequest>(std::bind(&Server::onQueueUnBind,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<basicPublishRequest>(std::bind(&Server::onBasicPublish,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<basicAckRequest>(std::bind(&Server::onBasicAck,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<basicConsumeRequest>(std::bind(&Server::onBasicConsume,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _dispatcher.registerMessageCallback<basicCancelRequest>(std::bind(&Server::onBasicCancel,this,
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));

            _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage,_codec.get(),
            std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));
            _server.setConnectionCallback(std::bind(&Server::onConnection,this,std::placeholders::_1));
        }

        void start()
        {
            _server.start();
            _baseloop.loop();
        }

    private:
        void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn,const MessagePtr& message,muduo::Timestamp)
        {
            LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
            conn->shutdown();
        }

        /*打开信道*/
        void onOpenChannel(const muduo::net::TcpConnectionPtr& conn,const openChannelRequestPtr& message,muduo::Timestamp)
        {
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {

                DLOG("打开信道时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            return self_conn->openChannel(message);
        }

        /*关闭信道*/
        void onCloseChannel(const muduo::net::TcpConnectionPtr& conn,const closeChannelRequestPtr& message,muduo::Timestamp)
        {
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {

                DLOG("关闭信道时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            return self_conn->closeChannel(message);
        }

        /*声明交换机*/
        void onDeclareExchange(const muduo::net::TcpConnectionPtr& conn,const declareExchangeRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("声明交换机时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("声明交换机时,没有找到对应的信道句柄");
                return;
            }
            return channel->declareExchange(message);
        }

        /*删除交换机*/
        void onDeleteExchange(const muduo::net::TcpConnectionPtr& conn,const deleteExchangeRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("删除交换机时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("删除交换机时,没有找到对应的信道句柄");
                return;
            }
            return channel->deleteExchange(message);
        }

        /*声明队列*/
        void onDeclareQueue(const muduo::net::TcpConnectionPtr& conn,const declareQueueRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("声明队列时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("声明队列时,没有找到对应的信道句柄");
                return;
            }
            return channel->declareQueue(message);
        }

        void onDeleteQueue(const muduo::net::TcpConnectionPtr& conn,const deleteQueueRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("删除队列时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("删除队列时,没有找到对应的信道句柄");
                return;
            }
            return channel->deleteQueue(message);
        }

        /*队列绑定*/
        void onQueueBind(const muduo::net::TcpConnectionPtr& conn,const queueBindRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("绑定队列时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("绑定队列时,没有找到对应的信道句柄");
                return;
            }
            return channel->queueBind(message);
        }

        /*队列解绑*/
        void onQueueUnBind(const muduo::net::TcpConnectionPtr& conn,const queueUnBindRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("解除绑定时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("解除绑定时,没有找到对应的信道句柄");
                return;
            }
            return channel->queueUnBind(message);
        }

        /*消息发布*/
        void onBasicPublish(const muduo::net::TcpConnectionPtr& conn,const basicPublishRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("消息发布时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("消息发布时,没有找到对应的信道句柄");
                return;
            }
            return channel->basicPublish(message);
        }

        /*消息确认*/
        void onBasicAck(const muduo::net::TcpConnectionPtr& conn,const basicAckRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("消息确认时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("消息确认时,没有找到对应的信道句柄");
                return;
            }
            return channel->basicAck(message);
        }

        /*订阅队列消息*/
        void onBasicConsume(const muduo::net::TcpConnectionPtr& conn,const basicConsumeRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("订阅队列消息时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("订阅队列消息时,没有找到对应的信道句柄");
                return;
            }
            return channel->basicConsume(message);
        }

        /*队列消息取消订阅*/
        void onBasicCancel(const muduo::net::TcpConnectionPtr& conn,const basicCancelRequestPtr& message,muduo::Timestamp)
        {
            //1.判断链接是否存在
            Connection::ptr self_conn=_connection_manager->getConnection(conn);
            if(self_conn.get()==nullptr)
            {
                DLOG("队列消息取消订阅时,没有找到连接对应的Connection句柄");
                conn->shutdown();
                return;
            }
            //2.判断信道是否存在
            Channel::ptr channel=self_conn->getChannel(message->cid());
            if(channel.get()==nullptr)
            {
                DLOG("队列消息取消订阅时,没有找到对应的信道句柄");
                return;
            }
            return channel->basicCancel(message);
        }

        /*链接建立成功和释放成功回调*/
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            if(conn->connected()==true)
            {
                _connection_manager->newConnection(_host,_consumer_manager,_codec,conn,_pool);
            }
            else
            {
                _connection_manager->delConnection(conn);
            }
        }
    private:
        muduo::net::EventLoop _baseloop;//事件循环对象
        muduo::net::TcpServer _server;//服务器对象
        ProtobufDispatcher _dispatcher;//protobuf协议分发器
        ProtobufCodecPtr _codec;//protobuf协议处理器句柄
        VirtualHost::ptr _host;//虚拟机对象句柄
        ConsumerManager::ptr _consumer_manager;//消费者管理对象句柄
        ConnectionManager::ptr _connection_manager;//链接管理对象句柄
        ThreadPool::ptr _pool;//线程池管理句柄
    };
}

#endif